package com.tyk.bookstore.front.payment.service.impl;


import cn.dev33.satoken.stp.StpUtil;
import com.alipay.easysdk.factory.Factory;
import com.tyk.bookstore.front.common.core.convert.StringToLocalDateTimeConverter;
import com.tyk.bookstore.front.common.core.model.enume.PaymentWay;
import com.tyk.bookstore.front.common.core.model.exception.CustomException;
import com.tyk.bookstore.front.common.core.util.AssertUtil;
import com.tyk.bookstore.front.common.core.util.IdGenerator;
import com.tyk.bookstore.front.common.core.util.ObjUtil;
import com.tyk.bookstore.front.member.service.MemberApiService;
import com.tyk.bookstore.front.order.model.dto.Order4PaymentDto;
import com.tyk.bookstore.front.order.model.query.Order4PayNotifyQuery;
import com.tyk.bookstore.front.order.service.OrderApiService;
import com.tyk.bookstore.front.payment.model.entity.AmountLogDo;
import com.tyk.bookstore.front.payment.model.entity.PaymentRecordDo;
import com.tyk.bookstore.front.payment.model.entity.ShopStatementDo;
import com.tyk.bookstore.front.payment.model.enume.AmountLogType;
import com.tyk.bookstore.front.payment.model.enume.ShopStatementType;
import com.tyk.bookstore.front.payment.model.enume.StatementNature;
import com.tyk.bookstore.front.payment.model.query.MemberRechargeQuery;
import com.tyk.bookstore.front.payment.model.query.OrderPayQuery;
import com.tyk.bookstore.front.payment.model.query.PayCallbackQuery;
import com.tyk.bookstore.front.payment.model.result.PaymentResultEnum;
import com.tyk.bookstore.front.payment.model.vo.MemberRechargeVo;
import com.tyk.bookstore.front.payment.repository.AmountLogRepository;
import com.tyk.bookstore.front.payment.repository.PaymentRecordRepository;
import com.tyk.bookstore.front.payment.repository.ShopStatementRepository;
import com.tyk.bookstore.front.payment.service.PaymentService;
import com.tyk.bookstore.front.payment.strategy.PayActionFactory;
import com.tyk.bookstore.front.payment.strategy.enume.PayType;
import com.tyk.bookstore.front.payment.strategy.query.PayQuery;
import com.tyk.bookstore.front.payment.util.MapStructPayment;
import com.tyk.bookstore.front.payment.util.PaymentUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;

/**
 * @author tyk
 * @description 支付管理
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PaymentServiceImpl implements PaymentService {

    private final OrderApiService orderApiService;
    private final MemberApiService memberApiService;
    private final PaymentRecordRepository paymentRecordRepository;
    private final AmountLogRepository amountLogRepository;
    private final StringToLocalDateTimeConverter converter;
    private final ShopStatementRepository shopStatementRepository;
    private final MapStructPayment mapStructPayment;


@Override
public String payOrder(OrderPayQuery query) {

    Long memberId = StpUtil.getLoginIdAsLong();

    // 查询订单信息
    Order4PaymentDto dto = orderApiService.getOrderWaitPay4PaymentDtoById(query.getOrderId(), memberId);

    // 支付类型
    PayType type = PaymentUtil.toPayType(query.getPaymentWay());
    AssertUtil.notNull(type, PaymentResultEnum.WAY_NOT_SUPPORT);

    // 支付参数
    PayQuery pq = new PayQuery()
            .setTradNo(dto.getId())
            .setSubject(dto.getTitle())
            .setAmount(dto.getAmount())
            .setPrincipal(memberId)
            .setCertificate(query.getPaymentPassword());

    // 支付
    return switch (query.getPaymentWay()) {
        case LOCAL, WE_CHAT -> {
            PayCallbackQuery pcq = new PayCallbackQuery();
            pcq.setOrderId(dto.getId())
                    .setAmount(dto.getAmount())
                    .setMemberId(dto.getMemberId())
                    .setPaymentWay(query.getPaymentWay())
                    .setPaymentTime(LocalDateTime.now());
            yield PayActionFactory.build(type).pay(
                    pq,
                    buildCallback(pcq)
            );
        }
        case ALIPAY -> PayActionFactory.build(type).pay(pq);
    };

}

    @Override
    public MemberRechargeVo recharge(MemberRechargeQuery query) {
        long memberId = StpUtil.getLoginIdAsLong();
        // 修改账号余额
        memberApiService.increaseAmount(memberId, query.getAmount());
        // 添加账号余额记录
        AmountLogDo ald = new AmountLogDo()
                .setId(IdGenerator.id())
                .setMemberId(memberId)
                .setModification(query.getAmount())
                .setType(AmountLogType.RECHARGE)
                .setCreateTime(LocalDateTime.now());
        amountLogRepository.insert(ald);
        return new MemberRechargeVo();
    }


    @Override
public void paySuccessAliCallback(HashMap<String, String> map) {

    log.info("支付宝支付回调:订单号=({})", map.get("out_trade_no"));

    // 订单号
    Long orderId = Long.valueOf(map.get("out_trade_no"));

    // 查询订单信息
    Order4PaymentDto dto = orderApiService.getOrderWaitPay4PaymentDtoById(orderId);

    // 订单金额
    BigDecimal amount = BigDecimal.valueOf(
            Double.parseDouble(map.get("total_amount"))
    );
    AssertUtil.equal(dto.getAmount(), amount, PaymentResultEnum.PAY_FAILED);

    // 支付宝流水号
    String tradeNo = map.get("trade_no");

    // 支付时间
    LocalDateTime paymentTime = converter.convert(map.get("gmt_payment"));

    // 验签
    try {
        Boolean f = Factory.Payment.Common().verifyNotify(map);
        if (!f) throw new CustomException(PaymentResultEnum.PAY_FAILED);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

    // 执行支付回调
    PayCallbackQuery pcq = new PayCallbackQuery();
    pcq.setOrderId(dto.getId())
            .setAmount(dto.getAmount())
            .setMemberId(dto.getMemberId())
            .setPaymentWay(PaymentWay.ALIPAY)
            .setPaymentTime(paymentTime)
            .setOutTradeNo(tradeNo);
    PayActionFactory.build(PayType.ALIPAY).callback(buildCallback(pcq));

}


    /**
     * 支付成功后的回调
     *
     * @param query 支付回调参数
     * @return 回调
     */
    private @NotNull Runnable buildCallback(PayCallbackQuery query) {
        return () -> {
            // 支付记录
            PaymentRecordDo prd = mapStructPayment
                    .toPaymentRecordDo(query)
                    .setId(IdGenerator.id())
                    .setTradeNo(PaymentUtil.paymentNo());
            paymentRecordRepository.insert(prd);
            // 账号余额变更记录
            if (
                    ObjUtil.equal(query.getPaymentWay(), PaymentWay.LOCAL) ||
                            ObjUtil.equal(query.getPaymentWay(), PaymentWay.WE_CHAT)
            ) {
                AmountLogDo ald = new AmountLogDo()
                        .setId(IdGenerator.id())
                        .setMemberId(query.getMemberId())
                        .setSourceIdentification(query.getOrderId())
                        .setModification(query.getAmount().negate())
                        .setNature(StatementNature.EXPENDITURE)
                        .setType(AmountLogType.ORDER_PAYMENT)
                        .setCreateTime(query.getPaymentTime());
                amountLogRepository.insert(ald);
            }
            // 书店流水
            ShopStatementDo ssd = new ShopStatementDo()
                    .setId(IdGenerator.id())
                    .setOtherSideIdentification(query.getMemberId())
                    .setSourceIdentification(query.getOrderId())
                    .setModification(query.getAmount())
                    .setNature(StatementNature.INCOME)
                    .setType(ShopStatementType.PRODUCT_SALE)
                    .setCreateTime(query.getPaymentTime());
            shopStatementRepository.insert(ssd);
            // 修改订单状态
            Order4PayNotifyQuery notifyQuery = new Order4PayNotifyQuery()
                    .setOrderId(query.getOrderId())
                    .setMemberId(query.getMemberId())
                    .setPaymentWay(query.getPaymentWay());
            orderApiService.payNotify(notifyQuery);

        };
    }

}
